package org.example.service;

import cn.hutool.core.thread.ThreadUtil;
import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * RetryService
 *
 * @author: wangchao
 * @Date: 2025/6/11 11:07
 * @since 7.0.0-RC2
 **/
@Slf4j
@Service
public class RetryService {
    private static final int resetThreshold = 8; // 重置阈值
    private static final int initialDelayMillis = 100; // 初始等待时间 (毫秒)
    private static final long maxDelayMillis = 1000 * ((long) Math.pow(2, resetThreshold)); // 最大等待时间 (毫秒)
    private static final int maxRetries = Integer.MAX_VALUE; // 最大重试次数

    public <T> T executeWithRetry(RetryableOperation<T> operation) {
        AtomicInteger retryCount = new AtomicInteger(0);
        AtomicInteger consecutiveFailures = new AtomicInteger(0);
        long currentDelay;
        while (retryCount.get() <= maxRetries) {
            try {
                // 执行操作
                T result = operation.execute();
                // 成功时重置连续失败计数
                consecutiveFailures.set(0);
                return result;
            } catch (Exception e) {
                retryCount.incrementAndGet();
                consecutiveFailures.incrementAndGet();
                int currentRetry = retryCount.get();
                if (currentRetry > maxRetries) {
                    log.error("Max retries {} reached, giving up", maxRetries, e);
                    throw new RetryException("Max retries reached", e);
                }
                // 计算指数退避延迟
                currentDelay = calculateDelay(consecutiveFailures.get());
                // 等待
                ThreadUtil.safeSleep(currentDelay);
                log.warn("retry failed {} and wait {} ms. failed reason: {}", currentRetry, currentDelay, e.getMessage());
                // 达到重置阈值时重置退避
                if (consecutiveFailures.get() >= resetThreshold) {
                    log.info("Resetting backoff after {} consecutive failures", resetThreshold);
                    consecutiveFailures.set(0);
                }
            }
        }
        throw new RetryException("Max retries reached without success");
    }

    /**
     * 计算指数退避延迟
     */
    private long calculateDelay(int failureCount) {
        long newDelay = Math.min(initialDelayMillis * ((long) Math.pow(2, failureCount - 1)), maxDelayMillis);
        log.debug("Calculated delay: {} ms (failure count: {})", newDelay, failureCount);
        return newDelay;
    }

    /**
     * 可重试操作接口
     */
    @FunctionalInterface
    public interface RetryableOperation<T> {
        T execute() throws Exception;
    }
}
